package com.net.xpay.core.service.impl;

import com.google.common.base.Strings;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserShard;
import com.net.xpay.common.domain.sync.CommonUserWithdrawRecord;
import com.net.xpay.common.enums.config.CommonSysConfigKey;
import com.net.xpay.common.helper.CommonSysConfigHelper;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserShardManager;
import com.net.xpay.common.manager.sync.CommonUserWithdrawRecordManager;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.TransBizTypeConstant;
import com.net.xpay.core.domain.*;
import com.net.xpay.core.enums.AccountBillType;
import com.net.xpay.core.enums.PayTransBatchStatus;
import com.net.xpay.core.enums.PayTransStatus;
import com.net.xpay.core.enums.UserWithdrawStatus;
import com.net.xpay.core.manager.PayTransBatchManager;
import com.net.xpay.core.manager.PayTransManager;
import com.net.xpay.core.manager.UserAccountManager;
import com.net.xpay.core.manager.UserWithdrawRecordManager;
import com.net.common.annotation.WriteTransactional;
import com.net.common.exception.BusinessException;
import com.net.common.util.MoneyUtil;
import com.net.common.util.SerializationUtil;
import com.net.xpay.common.validator.UserWithdrawVaildator;
import com.net.xpay.core.domain.*;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.helper.AccountDetailHelper;
import com.net.xpay.core.manager.*;
import com.net.xpay.core.model.InnerPayTransBatchModel;
import com.net.xpay.core.model.PayTransModel;
import com.net.xpay.core.model.UserWithdrawModel;
import com.net.xpay.core.service.AccountService;
import com.net.xpay.core.service.InnerPayTransService;
import com.net.xpay.core.service.UserWithdrawService;
import com.net.xpay.core.utils.GenIdUtil;
import com.net.shard.ShardUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 86188
 */
@Slf4j
@Service
public class UserWithdrawServiceImpl implements UserWithdrawService {
    @Autowired
    private UserWithdrawVaildator userWithdrawVaildator;

    @Autowired
    private UserAccountManager userAccountManager;

    @Autowired
    private AccountService accountService;

    @Autowired
    private UserWithdrawRecordManager userWithdrawRecordManager;

    @Autowired
    private AccountDetailHelper accountDetailHelper;

    @Autowired
    private CommonUserWithdrawRecordManager commonUserWithdrawRecordManager;

    @Autowired
    private UserShardManager userShardManager;

    @Autowired
    private CommonSysConfigHelper commonSysConfigHelper;

    @Autowired
    private UserManager userManager;

    @Autowired
    private InnerPayTransService innerPayTransService;

    @Autowired
    private PayTransBatchManager payTransBatchManager;

    @Autowired
    private PayTransManager payTransManager;

    @Override
    @WriteTransactional
    public UserWithdrawRecord addUserWithdrawRecord(User merchant, UserWithdrawModel model) {
        //验证
        userWithdrawVaildator.validateWithdrawInfo(merchant, model);

        UserAccount userAccount = userAccountManager.getByIdWithLock(merchant.getId());
        //提现金额 单位分
        Long amount = model.getAmount();
        if (amount == null) {
            amount = userAccount.getMoneyLeft();
        }
        if (amount < 100L) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "提现金额至少为1元");
        }
        if (amount > userAccount.getMoneyLeft()) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "服务费余额不足");
        }


        //新增
        UserWithdrawRecord userWithdrawRecord = doAddUserWithdrawRecord(merchant, model, amount);

        //扣减账户余额
        AccountDetail accountDetail = AccountDetail.getInstance(merchant.getId(), userWithdrawRecord.getId());
        accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.USER_WITHDRAW_REFUND, -amount, "");
        accountService.onPay(merchant, userAccount, accountDetail);

        return userWithdrawRecord;
    }

    @Override
    public String transUserWithdrawRecord(Long id) {
        CommonUserWithdrawRecord record = commonUserWithdrawRecordManager.getById(id);
        if (record == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "提现记录不存在");
        }
        if (record.getPayStatus() != UserWithdrawStatus.WAIT_PAY && record.getPayStatus() != UserWithdrawStatus.PAYING) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "提现状态不正确");
        }

        String alipayAccount = commonSysConfigHelper.getString(CommonSysConfigKey.PARTNER_WITHDRAW_ALIPAY_ACCOUNT);
        String sysMobile = commonSysConfigHelper.getString(CommonSysConfigKey.PARTNER_WITHDRAW_MOBILE);
        if (Strings.isNullOrEmpty(alipayAccount) || Strings.isNullOrEmpty(sysMobile)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请配置系统打款账号");
        }

        User transUser = userManager.findByMobile(sysMobile);
        if (transUser == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统打款账号不存在");
        }
        UserShard shard = userShardManager.getById(transUser.getId());

        try {
            ShardUtil.setShardContext(shard.getShardId());
            return doTransUserWithdrawRecord(record, alipayAccount, sysMobile, transUser);
        } finally {
            ShardUtil.cleanShardContext();
        }
    }

    @Override
    public void updateUserWithdrawResult(CommonUserWithdrawRecord record, User transUser) {
        PayTransBatch payTransBatch = payTransBatchManager.findByImportBatchNo(transUser.getId(), record.getBizNo());

        if (payTransBatch == null) {
            log.error("updateUserWithdrawResult error importBatchNo={}", record.getBizNo());
            return;
        }
        if (!PayTransBatchStatus.isFinalStatus(payTransBatch.getStatus())) {
            return;
        }

        List<PayTrans> payTrans = payTransManager.listByPayTransBatchId(transUser.getId(), payTransBatch.getId());

        doUpdateUserWithdrawResult(payTrans.get(0), record.getUserId(), record.getId(), record.getBizNo());
    }

    @Override
    @WriteTransactional
    public void closeUserWithdraw(Long id, String remark) {
        CommonUserWithdrawRecord record = commonUserWithdrawRecordManager.getById(id);
        if (record.getPayStatus() != UserWithdrawStatus.WAIT_PAY) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "待转账状态才可以关闭");
        }

        doCloseUserWithdraw(id, remark);
    }

    @Override
    public void confirmOfflineUserWithdraw(Long id, String remark) {
        CommonUserWithdrawRecord selectRecord = commonUserWithdrawRecordManager.getById(id);
        if (selectRecord.getPayStatus() != UserWithdrawStatus.WAIT_PAY) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "待转账状态才可以确认线下转账");
        }

        UserWithdrawRecord record = userWithdrawRecordManager.getById(id);
        if (record.getPayStatus() != UserWithdrawStatus.WAIT_PAY) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "待转账状态才可以确认线下转账");
        }
        record.setPayStatus(UserWithdrawStatus.PAYED);
        record.setEndTime(LocalDateTime.now());
        record.setErrorMsg(remark);
        userWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);
    }

    private void doCloseUserWithdraw(Long id, String remark) {
        UserWithdrawRecord record = userWithdrawRecordManager.getById(id);
        if (record.getPayStatus() != UserWithdrawStatus.WAIT_PAY) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "待转账状态才可以关闭");
        }
        record.setPayStatus(UserWithdrawStatus.PAY_FAILED);
        record.setErrorMsg(remark);
        record.setEndTime(LocalDateTime.now());
        userWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);

        User merchant = userManager.getById(record.getUserId());
        UserAccount userAccount = userAccountManager.getByIdWithLock(merchant.getId());
        //退款
        AccountDetail accountDetail = AccountDetail.getInstance(merchant.getId(), record.getId());
        accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.USER_WITHDRAW_FAIL, record.getTransferMoney(), "");
        accountService.onPay(merchant, userAccount, accountDetail);
    }

    private void doUpdateUserWithdrawResult(PayTrans payTrans, Long userId, Long id, String bizNo) {
        try {
            UserShard userShard = userShardManager.getById(userId);
            ShardUtil.setShardContext(userShard.getShardId());
            UserWithdrawRecord record = userWithdrawRecordManager.getById(id);
            if (PayTransStatus.SUCCESS == payTrans.getStatus()) {
                if (record.getEndTime() == null) {
                    record.setBizNo(bizNo);
                    record.setPayStatus(UserWithdrawStatus.PAYED);
                    record.setEndTime(LocalDateTime.now());
                    record.setErrorMsg(null);
                    record.setErrorCode(null);
                    userWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);
                }
            } else {
                record.setBizNo(bizNo);
                record.setErrorMsg(payTrans.getErrorMsg());
                record.setErrorCode(payTrans.getErrorCode());
                userWithdrawRecordManager.updateByPrimaryKeyWithVersion(record);
            }
        } finally {
            ShardUtil.cleanShardContext();
        }
    }



    private String doTransUserWithdrawRecord(CommonUserWithdrawRecord record, String alipayAccount, String sysMobile, User transUser) {
        PayTransBatch batch = payTransBatchManager.findByImportBatchNo(transUser.getId(), record.getBizNo());
        log.info("doTransUserWithdrawRecord batch={}", SerializationUtil.obj2String(batch));
        if (batch != null) {
            if (batch.getStatus() == PayTransBatchStatus.SUCCESS) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "提现订单已付款");
            }
            //还是待付款的 取之前的链接就可以
            if (batch.getStatus() == PayTransBatchStatus.WAIT_PAY) {
                return batch.getLastPayUrl();
            } else if (batch.getStatus() == PayTransBatchStatus.FAIL || batch.getStatus() == PayTransBatchStatus.DISUSE){
                //重新生成一个订单号
                record.setBizNo(GenIdUtil.getId(TransBizTypeConstant.UW, transUser.getId()).substring(0, 30));
            }
        }


        InnerPayTransBatchModel payTransBatchModel = new InnerPayTransBatchModel();
        payTransBatchModel.setAlipayNickName(alipayAccount);
        payTransBatchModel.setMobile(sysMobile);
        payTransBatchModel.setImportBatchNo(record.getBizNo());
        List<PayTransModel> payTrans = new ArrayList<>();

        PayTransModel payTransModel = new PayTransModel();
        payTransModel.setImportBizNo(record.getId().toString());
        payTransModel.setTransAmount(MoneyUtil.convertCentToString(record.getTransferMoney(), 2));
        payTransModel.setRemark("退款");
        payTransModel.setIdentity(record.getAlipayNickName());
        payTransModel.setName(record.getRealName());
        payTrans.add(payTransModel);

        payTransBatchModel.setPayTrans(payTrans);
        String payUrl = innerPayTransService.batchPayTrans(payTransBatchModel);
        if (!Strings.isNullOrEmpty(payUrl)) {
            record.setPayStatus(UserWithdrawStatus.PAYING);
            commonUserWithdrawRecordManager.update(record);
        }

        return payUrl;
    }


    private UserWithdrawRecord doAddUserWithdrawRecord(User merchant, UserWithdrawModel model, Long moneyLeft) {
        UserWithdrawRecord userWithdrawRecord = UserWithdrawRecord.getInstance();

        userWithdrawRecord.setUserId(merchant.getId());
        userWithdrawRecord.setTransferMoney(moneyLeft);
        userWithdrawRecord.setRealName(model.getRealName().trim());
        userWithdrawRecord.setAlipayNickName(model.getAlipayNickName().trim());
        userWithdrawRecordManager.save(userWithdrawRecord);

        return userWithdrawRecord;
    }
}
